#ifndef T_HTTPREPLY_H
#define T_HTTPREPLY_H

#include "core/core.h"

class T_HttpReply_1 : public NetworkHttpReply
{
	Q_OBJECT
public:
    explicit T_HttpReply_1(HttpAccessBackend *parent);
	// raw headers:
	virtual bool hasRawHeader(const QByteArray &headerName) const;
	virtual QList<QByteArray> rawHeaderList() const;
	virtual QByteArray rawHeader(const QByteArray &headerName) const;
	// attributes
	virtual QVariant attribute(QNetworkRequest::Attribute code) const;
	// reading
	virtual qint64 bytesAvailable();
    virtual QByteArray read(qint64 a_max);
    // errorString()
    virtual QString errorString() const;
    // SSL
    virtual void ignoreSslErrors();
    virtual void ignoreSslErrors(const QList<QSslError> & errors);
    virtual QSslConfiguration sslConfiguration() const;
    virtual void setSslConfiguration(const QSslConfiguration & config);
protected:
    QNetworkReply *m_netReply;
};

class T_HttpReply_3 : public NetworkHttpReply
{
    Q_OBJECT
public:
    QNetworkReply *m_headReply;
    explicit T_HttpReply_3(HttpAccessBackend *backend);
    // raw headers:
    virtual bool hasRawHeader(const QByteArray &headerName) const;
    virtual QList<QByteArray> rawHeaderList() const;
    virtual QByteArray rawHeader(const QByteArray &headerName) const;
    // attributes
    virtual QVariant attribute(QNetworkRequest::Attribute code) const;
    // reading
    virtual qint64 bytesAvailable();
    virtual QByteArray read(qint64 a_max);
    class T_HttpReply_3Private *d_ptr;
    Q_DECLARE_PRIVATE(T_HttpReply_3);
public slots:
    void head_readyRead();
    void head_finished();
};

class T_Partial_Reply : public QObject
{
    Q_OBJECT
public:
    explicit T_Partial_Reply(const QNetworkRequest &a_request, qint64 a_begin, qint64 a_length = -1)
        : m_request(a_request), m_begin(a_begin), m_length(a_length)
        , m_nam(0), m_reply(0), m_waiter(0), m_read_count(0), m_name("")
    {
        Q_ASSERT(m_begin>=0);
        Q_ASSERT(m_length!=0);
    }
    virtual ~T_Partial_Reply()
    {
        m_waiter->deleteLater();
        m_nam->deleteLater();
    }
    bool isActive()
    {
        return (m_waiter != NULL);
    }
    bool isFinished()
    {
        if(m_waiter == NULL)
        {
            return false;
        }
        return (m_waiter->isFinished());
    }
    qint64 bytesAvailable()
    {
        if(m_waiter == NULL)
        {
            return 0;
        }
        return (m_waiter->just_bytesAvailable());
    }
    QByteArray wait_readAll(qint64 a_break_size)
    {
        QByteArray v_result;
        if(m_waiter == NULL)
        {
            v_result = QByteArray();
        }
        else
        {
            v_result = m_waiter->wait_readAll(a_break_size);
        }
        m_read_count += v_result.size();
        return v_result;
    }
    QByteArray just_read(qint64 a_max)
    {
        QByteArray v_result;
        if(m_waiter == NULL)
        {
            v_result = QByteArray();
        }
        else
        {
            v_result = m_waiter->just_read(a_max);
        }
        m_read_count += v_result.size();
        return v_result;
    }
    void wake()
    {
        if(this->isActive())
        {
            return;
        }
        //[Range header]
        QString v_range = QString("bytes=%1-").arg(m_begin);
        if(m_length > 0)
        {
            v_range += QString::number(m_begin+m_length-1);
        }
        //qDebug() << v_range;
        m_name = v_range;
        m_request.setRawHeader("Range", v_range.toLatin1());
        //
        m_nam = new QNetworkAccessManager();
        m_reply = m_nam->get(m_request);
        m_waiter = new NetworkBuffer(m_reply);
        m_waiter->setName(QString("waiter@%1").arg(m_begin));
        connect(m_reply, SIGNAL(metaDataChanged()), this, SLOT(on_reply_metaDataChanged()));
        connect(m_reply, SIGNAL(readyRead()), this, SLOT(on_reply_readyRead()));
        connect(m_reply, SIGNAL(finished()), this, SLOT(on_reply_finished()));
    }
    QString name()
    {
        return m_name;
    }

public slots:
    void on_reply_metaDataChanged()
    {
        Q_ASSERT(m_reply);
        QVariant v_loc = m_reply->header(QNetworkRequest::LocationHeader);
        if(v_loc.isValid())
        {
            qDebug() << "[v_loc]" << v_loc;
            m_waiter->deleteLater();;
            m_waiter = NULL;
            m_nam->deleteLater();
            m_nam = NULL;
            m_reply = NULL;
            m_request.setUrl(v_loc.toUrl());
            qDebug() << "[goto try_again]";
            this->wake();
        }
    }
    void on_reply_readyRead()
    {
        Q_ASSERT(m_reply);
        QVariant v_loc = m_reply->header(QNetworkRequest::LocationHeader);
        if(!v_loc.isValid())
        {
            emit readyRead();
        }
    }
    void on_reply_finished()
    {
        Q_ASSERT(m_reply);
        QVariant v_loc = m_reply->header(QNetworkRequest::LocationHeader);
        if(!v_loc.isValid())
        {
            if(m_length>0)
            {
                //Q_ASSERT( m_length==(m_read_count+m_waiter->just_bytesAvailable()) );
                if( m_length!=(m_read_count+m_waiter->just_bytesAvailable()) )
                {
                    if(m_read_count+m_waiter->just_bytesAvailable()==0)
                    {
                        m_waiter->deleteLater();;
                        m_waiter = NULL;
                        m_nam->deleteLater();
                        m_nam = NULL;
                        m_reply = NULL;
                        ////m_request.setUrl(v_loc.toUrl());
                        qDebug() << "[goto try_again](2)";
                        this->wake();
                        return;
                    }
                    qDebug() << m_reply->header(QNetworkRequest::ContentTypeHeader);
                    qDebug() << m_reply->header(QNetworkRequest::ContentLengthHeader);
                    qDebug() << "[m_begin]" << m_begin;
                    qDebug() << "[m_length]" << m_length;
                    qDebug() << "[m_read_count]" << m_read_count;
                    qDebug() << "[m_waiter->just_bytesAvailable()]" << m_waiter->just_bytesAvailable();
                    //Q_ASSERT( m_length==(m_read_count+m_waiter->just_bytesAvailable()) );
                    qint64 v_part_len = (m_read_count+m_waiter->just_bytesAvailable());
                    QString v_range = QString("bytes=%1-").arg(m_begin+v_part_len);
                    if(m_length > 0)
                    {
                        v_range += QString::number(m_begin+m_length-1-v_part_len);
                    }
                    //qDebug() << v_range;
                    ////m_name = v_range;
                    m_request.setRawHeader("Range", v_range.toLatin1());
                    m_nam->deleteLater();
                    m_nam = NULL;
                    m_reply = NULL;
                    //
                    m_nam = new QNetworkAccessManager();
                    m_reply = m_nam->get(m_request);
                    m_waiter->setNewReply(m_reply);
                    connect(m_reply, SIGNAL(metaDataChanged()), this, SLOT(on_reply_metaDataChanged()));
                    connect(m_reply, SIGNAL(readyRead()), this, SLOT(on_reply_readyRead()));
                    connect(m_reply, SIGNAL(finished()), this, SLOT(on_reply_finished()));
                    qDebug() << "[goto try_again](3)";
                }
            }
            //qDebug() << "FINISH:" << m_name << m_length << m_read_count << m_waiter->bytesAvailable();
            if(m_waiter->just_bytesAvailable())
            {
                emit readyRead();
                //qDebug() << "FINISH(2):" << m_name << m_length << m_read_count << m_waiter->bytesAvailable();
            }
            emit finished();
        }
    }
protected:
    QNetworkRequest m_request;
    qint64 m_begin;
    qint64 m_length;
    QNetworkAccessManager *m_nam;
    QNetworkReply *m_reply;
    NetworkBuffer *m_waiter;
    qint64 m_read_count;
    QString m_name;
signals:
    void readyRead();
    void finished();
};

#endif // T_HTTPREPLY_H
